#include <iostream>
#include <string>
#include <cstdlib>
#include <cassert>
#include <unistd.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>

struct sockaddr_in server;

static void Usage(std::string name)
{
    std::cout << "Usage:\n\t" << name << " server_ip server_port" << std::endl;
}

void *recverAndPrint(void *args)
{
    while (true)
    {
        int sockfd = *(int *)args;
        char buffer[1024];
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        ssize_t s = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&temp, &len);
        if (s > 0)
        {
            buffer[s] = 0;
            std::cout << "server echo# " << buffer << std::endl;
        }
    }
}

// ./udpClient server_ip server_port
// 如果一个客户端要连接server必须知道server对应的ip和port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    // 1. 根据命令行，设置要访问的服务器IP
    std::string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    // 2. 创建客户端
    // 2.1 创建socket，服务器是udp的已经跑起来了，客户端也要想办法去连接服务器
    // 所以客户端也必须得有套接字信息
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sockfd > 0);

    // 2.2 client 需不需要bind？？？(不需要)需要bind，但是不需要用户自己bind，而是OS自动给你bind
    // 所谓的"不需要"，指的是: 不需要用户自己bind端口信息！因为OS会自动给你绑定，你也最好这么做！
    // 如果我非要自己bind呢？可以！严重不推荐！
    // 所有的客户端软件 <-> 服务器 在进行通信的时候，必须得有 client[ip:port] <-> server[ip:port]
    // 为什么呢？？
    // 因为client很多，不能给客户端bind指定的port，port可能被别的client使用了
    // 一旦被别的client使用了你的client就无法启动了，因为一个客户端只能被一个进程绑定
    // 只能让操作系统随机生成端口号，用的时候拿去用，不用了就回收掉，下次客户端再来再把端口号给别的客户端

    // 那么server凭什么要bind呢？？
    // server提供的服务，必须被所有人知道！server不能随便改变！
    // client端口号是多少一点都不重要，只需要保证唯一性就可以，因为没人连它

    // 2.2 填写服务器对应的信息
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());

    // 创建一个线程就可以了
    pthread_t t;
    pthread_create(&t, nullptr, recverAndPrint, (void *)&sockfd);

    // 3. 通讯过程
    std::string buffer;
    while (true)
    {
        std::cerr << "Please Enter# ";
        std::getline(std::cin, buffer);

        // 发送消息给server:
        // 客户端首次调用sendto函数的时候，我们的client会自动bind自己的ip和port
        sendto(sockfd, buffer.c_str(), buffer.size(), 0,\
               (const struct sockaddr *)&server, sizeof(server)); 

        // // 发完消息之后再转发回去
        // char buffer[1024];
        // struct sockaddr_in temp;
        // socklen_t len = sizeof(temp);
        // ssize_t s = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&temp, &len);

        // if (s > 0)
        // {
        //     buffer[s] = 0;
        //     std::cout << "server echo# " << buffer << std::endl; 
        // }
    }

    close(sockfd);

    return 0;
}